package com.franklin.ideaplugin.api.classloader;

import cn.hutool.core.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author Franklin
 * @since 2023/08/27 16:50
 */
public class ProjectMethodInvoker {

    /**
     * 工程类加载器
     */
    private final ProjectClassLoader projectClassLoader;

    /**
     * 需要加载的类
     */
    private final String classQualifiedName;

    /**
     * 构造器参数类型
     */
    private final Class<?>[] constructorParamTypes;

    /**
     * 构造器参数
     */
    private final Object[] constructorArgs;

    /**
     * 对象类
     */
    private Class<?> targetClass;

    /**
     * 对象
     */
    private Object target;

    public ProjectMethodInvoker(
            @NotNull ProjectClassLoader projectClassLoader,
            @NotNull String classQualifiedName
    ) throws Throwable {
        this(projectClassLoader,classQualifiedName,null,null);
    }

    public ProjectMethodInvoker(
            @NotNull ProjectClassLoader projectClassLoader,
            @NotNull String classQualifiedName,
            @Nullable Class<?>[] constructorParamTypes,
            @Nullable Object[] constructorArgs
    ) throws Throwable {
        this.projectClassLoader = projectClassLoader;
        this.classQualifiedName = classQualifiedName;
        if (ArrayUtil.isEmpty(constructorParamTypes)) {
            constructorParamTypes = new Class<?>[]{};
        }
        if (ArrayUtil.isEmpty(constructorArgs)) {
            constructorArgs = new Object[]{};
        }
        this.constructorParamTypes = constructorParamTypes;
        this.constructorArgs = constructorArgs;

        this.init();
    }

    /**
     * 初始化
     *
     * @throws Throwable
     */
    private void init() throws Throwable {
        this.targetClass = this.projectClassLoader.loadClass(this.classQualifiedName);
        Constructor<?> declaredConstructor = this.targetClass.getDeclaredConstructor(this.constructorParamTypes);
        this.target = declaredConstructor.newInstance(this.constructorArgs);
    }

    /**
     * 执行方法
     *
     * @param methodName
     * @param parameterTypes
     * @param parameters
     * @return
     */
    public Object invoke(String methodName, Class<?>[] parameterTypes, Object[] parameters) {
        try {
            Method declaredMethod = this.targetClass.getMethod(methodName, parameterTypes);
            declaredMethod.setAccessible(true);
            return declaredMethod.invoke(this.target, parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public Object invoke(String methodName, String[] parameterTypes, Object[] parameters) {
        try {
            Class<?>[] classes = new Class<?>[parameterTypes.length];
            for (int i = 0; i < parameterTypes.length; i++) {
                classes[i] = projectClassLoader.loadClass(parameterTypes[i]);
            }
            return invoke(methodName,classes,parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public Object getTarget() {
        return target;
    }
}
